// ForestCamera.m -- render a scene of 1 (or someday more) trees

#import <appkit/appkit.h>
#import "ForestCamera.h"
#import "Tree.h"

/* 4/7/93 -- Derrived from /NextDeveloper/Examples/3Dkit/SimpleCamera 
	(by Bill Bumgarner 6/1/92 with assistance from Dave Springer)
   5/9/93 -- Derrived from Simple (By Allen King)
 */

@implementation ForestCamera

- awakeFromNib
{	return [self newParameter :nil];	// set values in parameter window
}

- initFrame :(const NXRect *) theRect
{	id ambientLight, pointLight;
	RtPoint xAxis = {1.0, 0.0, 0.0};
	RtPoint yAxis = {0.0, 1.0, 0.0};
	RtPoint lFromP = {0.5,0.5,0.75};				// light position point
	RtPoint fromP = {10.0,3.0,5.0}, toP = {0,0,0};	// camera position
	[super initFrame :theRect];
	
	 // initialize camera
	[self setEyeAt :fromP toward :toP roll :90.0];
	redraw_mode = 1;
	dirty = 0;

	 // create a shader that will shade surfaces with a simple matte surface.
	globalShader = [[N3DShader alloc] init];
	[(N3DShader *)globalShader setShader :"matte"];
	
	 // initialize 1 tree in this ForestCamera
	theTree = [[Tree alloc] init];
	[theTree plantIn :self];

	 // create an ambientlight source.
	ambientLight=[[N3DLight alloc] init];
	[ambientLight makeAmbientWithIntensity :0.25];
	[self addLight :ambientLight];
	
	 // create a Point light and put at lFromP at full intensity (1.0).
	pointLight = [[N3DLight alloc] init];
	[pointLight makePointFrom :lFromP intensity :2.0];
	[self addLight :pointLight];
	
	 // set the surface type to generate smooth solids. 
	[self setSurfaceTypeForAll :N3D_SmoothSolids chooseHider :YES];
	
	 // the N3DRotator object that governs rotational control via the mouseDown
	theRotator=[[N3DRotator alloc] initWithCamera :self];
	
	return self;
}

- newParameter :sender;
{	int oldBranchFactor=[theTree branchFactor], oldLevels=[theTree levels];
	int SDoldBranchFactor=[theTree SDbranchFactor];
	int SDoldLevels=[theTree SDlevels];
	int update=1;

	if (sender == levelsText || sender == levelsSlider)
		if ([theTree levels :[sender intValue]] == oldLevels)
			update = 0;
	if (sender == branchFactorText || sender == branchFactorSlider)
		if ([theTree branchFactor :[sender intValue]] == oldBranchFactor)
			update = 0;
	if (sender==angleText || sender==angleSlider)
		[theTree angle :[sender doubleValue]];
	if (sender == twistText || sender == twistSlider)
		[theTree twist :[sender doubleValue]];
	if (sender == shrinkText || sender == shrinkSlider)
		[theTree shrink :[sender doubleValue]];

	if (sender == SDbranchFactorText || sender == SDbranchFactorSlider)
		if ([theTree SDbranchFactor :[sender doubleValue]] ==SDoldBranchFactor)
			update = 0;
	if (sender == SDlevelsText || sender == SDlevelsSlider)
		if ([theTree SDlevels :[sender doubleValue]] == SDoldLevels)
			update = 0;
	if (sender == SDangleText || sender == SDangleSlider)
		[theTree SDangle :[sender doubleValue]];
	if (sender == SDtwistText || sender == SDtwistSlider)
		[theTree SDtwist :[sender doubleValue]];
	if (sender == SDshrinkText || sender == SDshrinkSlider)
		[theTree SDshrink :[sender doubleValue]];

	[angleSlider setFloatValue :[theTree angle]];
	 [angleText setFloatValue :[theTree angle]];
	[twistSlider setFloatValue :[theTree twist]];
	 [twistText setFloatValue :[theTree twist]];
	[shrinkSlider setFloatValue :[theTree shrink]];
	 [shrinkText setFloatValue :[theTree shrink]];
	[SDangleSlider setFloatValue :[theTree SDangle]];
	 [SDangleText setFloatValue :[theTree SDangle]];
	[SDtwistSlider setFloatValue :[theTree SDtwist]];
	 [SDtwistText setFloatValue :[theTree SDtwist]];
	[SDshrinkSlider setFloatValue :[theTree SDshrink]];
	 [SDshrinkText setFloatValue :[theTree SDshrink]];
	if (update)
	{	[levelsSlider setIntValue :[theTree levels]];
		 [levelsText setIntValue :[theTree levels]];
		[branchFactorSlider setIntValue :[theTree branchFactor]];
		 [branchFactorText setIntValue :[theTree branchFactor]];
		[SDbranchFactorSlider setIntValue :[theTree SDbranchFactor]];
		 [SDbranchFactorText setIntValue :[theTree SDbranchFactor]];
		[SDlevelsSlider setIntValue :[theTree SDlevels]];
		 [SDlevelsText setIntValue :[theTree SDlevels]];

		dirty = 1;
		if (redraw_mode)
		{	[theTree plantIn :self];
			[self redrawNow :nil];
			dirty = 0;
	}	}
	return self;
}

#define ACTIVEBUTTONMASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK)
- mouseDown :(NXEvent *)theEvent
{
	int			oldMask;
	NXPoint		oldMouse, newMouse, dMouse;
	RtMatrix	rmat, irmat;
	
	[theRotator setRotationAxis :N3D_ZAxis];
	
	 // track the mouse until a mouseUp event occurs, updating the display
	 // as tracking happens.
	[self lockFocus];
	oldMask = [window addToEventMask :ACTIVEBUTTONMASK];
	
	 // switch to the N3D_WireFrame surface type.
	[self setSurfaceTypeForAll :N3D_WireFrame chooseHider :YES];
	
	oldMouse = theEvent->location;
	[self convertPoint :&oldMouse fromView :nil];
	while (1)
	{	newMouse = theEvent->location;
		[self convertPoint :&newMouse fromView :nil];
		dMouse.x = newMouse.x - oldMouse.x;
		dMouse.y = newMouse.y - oldMouse.y;
		if (dMouse.x != 0.0 || dMouse.y != 0.0) 
		{	[theRotator trackMouseFrom :&oldMouse to :&newMouse
			rotationMatrix :rmat andInverse :irmat];
			[worldShape concatTransformMatrix :rmat premultiply :NO];
			[self display];
		}
		theEvent = [NXApp getNextEvent :ACTIVEBUTTONMASK];
		if (theEvent->type == NX_MOUSEUP)
			break;
		oldMouse = newMouse;
	}
	 // switch back to the N3D_SmoothSolids surface type
	[self setSurfaceTypeForAll :N3D_SmoothSolids chooseHider :YES];	
	[self display];
	[self unlockFocus];
	
	[window setEventMask :oldMask];
	return self;
}

- redrawNow :sender;
{	[self lockFocus];
	[self setSurfaceTypeForAll :N3D_SmoothSolids chooseHider :YES];	
	[self display];
	[self unlockFocus];
	return self;
}

- redrawMode :sender;
{	int lastRedraw_mode = redraw_mode;
	redraw_mode = [sender selectedTag]? 0: 1;
	if (!(lastRedraw_mode &&  !redraw_mode))
	{	if (dirty)
			[theTree plantIn :self];
		dirty = 0;
		[self redrawNow :nil];
	}
	return self;
}

- randomMode :sender	{[theTree randomMode :sender]; return self;}
- specis :sender		{[theTree specis :sender]; return self;}
- (int)redraw_mode		{return redraw_mode;}

- dumpRib :sender
{
	static id savePanel=nil;
	NXStream *ts;
	char buf[MAXPATHLEN+1];
	
	// initialize the savePanel, if it hasn''t been done so previously
	if (!savePanel) 
	{	savePanel=[SavePanel new];
		[savePanel setRequiredFileType :"rib"];
	}
	
	// run the savepanel.  
	if([savePanel runModal])
	{	 // returned w/pathname, open a stream and
		ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
		 // process the file name for a custom display line such that
		 // "prman <<filename>>.rib" will put the resulting image somewhere
		 // predictably useful.
		strcpy(buf, [savePanel filename]);
		 // remove the .rib extension from the path returned by the SavePanel
		strrchr(buf,'.')[0]='\0';
		 // feed to NXPrintf to put in the custom Display command
		NXPrintf(ts, "Display \"%s.tiff\" \"file\" \"rgba\"\n", buf);
		 // then feed the rib code to the stream and
		[theTree copyRIBCode :ts];
		 // save the stream to the file selected in the savepanel
		NXSaveToFile(ts, [savePanel filename]);
		 // and close the stream (which also flushes it), also making sure
		 // that the allocated memory is freed.
		NXCloseMemory(ts,NX_FREEBUFFER);
	}
	return self;
}

@end
